﻿using System.IO.Compression;

namespace pdfer.Filters
{
    public class FlateDecodeFilter : IFilter
    {
        /// <inheritdoc/>
        public Byte[] Decode(ReadOnlySpan<Byte> input)
        {
            try
            {
                // https://stackoverflow.com/questions/39939869/data-format-for-system-io-compression-deflatestream
                // https://www.ietf.org/rfc/rfc1950.txt
                // 微软提供的官方库只能对没有头和尾的纯压缩数据进行解压
                // pdf中的flate压缩数据是带有头和尾的
                // 头有 2 bytes - Defines the compression mode
                // First byte(CMF):
                // bits 0 to 3  CM     Compression method
                // bits 4 to 7  CINFO Compression info
                // Second byte (FLG):
                // bits 0 to 4  FCHECK(check bits for CMF and FLG)
                // bit  5       FDICT(preset dictionary)
                // bits 6 to 7  FLEVEL(compression level)
                // 尾部 4 bytes of checksum
                // 
                using var output = new MemoryStream();
                using var compressedStream = new MemoryStream(input[2..^4].ToArray());
                using var zipStream = new DeflateStream(compressedStream, CompressionMode.Decompress);
                zipStream.CopyTo(output);
                zipStream.Flush();
                return output.ToArray();
            }
            catch (Exception ex)
            {
                return [];
            }
        }


        /// <inheritdoc/>
        public byte[] Encode(ReadOnlySpan<Byte> input)
        {
            try
            {
                // 文件头需要加 0x78 0x9C
                // 78 01 - No Compression / low
                // 78 5E - Fast Compression
                // 78 9C - Default Compression
                // 78 DA - Best Compression
                // 文件尾部计算 Adler-32检查和
                using var output = new MemoryStream();
                output.Write([0x78, 0x9C], 0, 2);

                using var inputStream = new MemoryStream(input.ToArray());
                using var zipStream = new DeflateStream(output, CompressionMode.Compress);
                inputStream.CopyTo(zipStream);
                zipStream.Flush();
                output.Write(Adler32(input), 0, 4);
                var data = output.ToArray();
                return data;
            }
            catch (Exception ex)
            {
                return [];
            }
        }

        /// <summary>
        /// 生成校验和
        /// </summary>
        /// <param name="data"></param>
        /// <returns></returns>
        private static Byte[] Adler32(ReadOnlySpan<Byte> data)
        {
            // https://www.ietf.org/rfc/rfc1950.txt
            UInt32 adler = 1;
            UInt32 s1 = adler & 0xffff;
            UInt32 s2 = (adler >> 16) & 0xffff;
            for (int i = 0; i < data.Length; i++)
            {
                s1 = (s1 + data[i]) % 65521;
                s2 += s1 % 65521;
            }
            return BitConverter.GetBytes((s2 << 16) + s1);
        }

        /// <inheritdoc/>
        public Byte[] Decode2(ReadOnlySpan<Byte> input)
        {
            try
            {
                // SharpZipLib 暂时不用这个库有点小问题 
                // using var output = new MemoryStream();
                // using var compressedStream = new MemoryStream(input[2..].ToArray());
                // using var inputStream = new InflaterInputStream(compressedStream);
                // inputStream.CopyTo(output);
                // return output.ToArray();
                throw new NotImplementedException();
            }
            catch (Exception ex)
            {
                return [];
            }
        }

        /// <inheritdoc/>
        public byte[] Encode2(ReadOnlySpan<Byte> input)
        {
            try
            {
                //using var output = new MemoryStream();
                //using var zipStream = new DeflaterOutputStream(output);
                //zipStream.Write(input);
                //zipStream.Flush();
                //zipStream.Finish();
                //return output.ToArray();
                throw new NotImplementedException();
            }
            catch (Exception ex)
            {
                return [];
            }
        }
    }
}
